using System;
using System.Collections.Generic;
using StarTrek.Space;
using StarTrek.Weapon;
using StarTrek.World;

namespace StarTrek.Ship
{
    public abstract class BaseShip : iShip
    {
        protected WeaponList cWeapon = null;
        protected float cPowerUnit = float.NaN;
        private iSpaceItem cCurrentLocation = null;
        const float ENGINE_MPG = 10; //NOTE: Simplification, all locations require 10 units to cross.

        protected BaseShip()
        {
            cWeapon = new WeaponList(this);
            cPowerUnit = 5000;
        }

        internal WeaponList Weapons
        {
            set
            {
                cWeapon = value;
            }
        }
        public float ShieldStrength
        {
            get
            {
                Shield local = cWeapon.GetWeaponType("Shield") as Shield;

                if (local != null)
                    return local.ShieldStrength;
                return 0; //FailSafe
            }
        }
        public float HullStrength
        {
            get
            {
                Repair local = cWeapon.GetWeaponType("Repair") as Repair;

                if (local != null)
                    return local.HullStrength;
                return 0; //FailSafe
            }
        }
        public float PowerUnit
        {
            get
            {
                return cPowerUnit;
            }
            set
            {
                cPowerUnit = value;
            }
        }

        public iSpaceItem CurrentLocation
        {
            get
            {
                return cCurrentLocation;
            }
            set
            {
                if (cCurrentLocation != null && cCurrentLocation.DockedShip == this)
                    cCurrentLocation.DockedShip = null;

                cCurrentLocation = value;
            }
        }
        public List<iWeapon> GetWeapons()
        {
            return cWeapon.InnerList;
        }

        public abstract string BuilderRace { get; }
        public abstract string ShipModel { get; }
        public abstract string ShipName { get; }

        /// <summary>
        /// Travel the current vessel to a new location in the galaxy
        /// </summary>
        /// <param name="Target">a valid terminal flight path locaiton </param>
        /// <exception cref="InvalidFlightPath"> InvalidFlightPath occurs when the destination is not a valid terminal location.</exception>
        public void Travel(iSpaceItem Target/*NOTE: may Want to add method of travel*/)
        {
            if(!Target.isTravelable) throw new InvalidFlightPath(this,Target);

            float Distance = CurrentLocation.CalculateDistance(Target);
            float EngeryRequired = Distance * ENGINE_MPG;       //Cost of fuel
            EngeryRequired += CurrentLocation.TerrainPenalty;   //Penality for leaving a location
            
            System.Diagnostics.Debug.WriteLine(
                string.Format(
                    "Traveling from [{3}]to new [{2}] location Power Reserves: {0:#,###.00} units Est Power Req: {1:#,###.00} units ",
                    PowerUnit, EngeryRequired, Target, CurrentLocation));

            //Remove ship from old location 
            CurrentLocation.PresentShip = null;            //Tell Location a ship left.
            if (CurrentLocation.isDockable)                //Then Also undock the ship at this locaiton.
            {
                CurrentLocation.DockedShip = null;
                //TODO: raise event that a un-docking event occured
                System.Diagnostics.Debug.WriteLine(string.Format("Ship unDocked at {0}", Target));
            }

            //Update Ship 
            PowerUnit -= EngeryRequired;                        //Decrement Engery

            //Update New location
            CurrentLocation = Target;                           //Move there
            Target.PresentShip = this;                          //Tell Location a ship arrived.

            if (Target.isDockable)                              //Then Also dock the ship at this locaiton.
            {
                Target.DockedShip = this;
                //TODO: raise event that a docking event occured
                System.Diagnostics.Debug.WriteLine(string.Format("Ship Docked at {0}",Target));
            }
        
        }

        public ShipStatus Offense(iShip Target, iWeapon Weapon, float Energy)
        {
            return Weapon.ApplyWeponToShip(Target, Energy);
        }

        public iShip Defense(iWeapon Weapon, float Energy)
        {
            Weapon.ApplyDefenseToShip(Energy);
            return this;
        }

        public iWeapon GetWeapon(string weapon)
        {
            return cWeapon.GetWeaponType(weapon);
        }

        public void ApplyDamage(float DamagePoints)
        {
            Shield ShipShield = cWeapon.GetWeaponType("Shield") as Shield; //cWeapon.Find(isShieldWeapon) as Shield;
            Repair ShipHull = cWeapon.GetWeaponType("Repair") as Repair; //cWeapon.Find(isRepairWeapon) as Repair;

            if (ShipShield != null && DamagePoints > 0)
                DamagePoints = ShipShield.DamageShield(DamagePoints);

            //if there are remaining DamagePoints, Randomly choose a weapon and disable - consuming remaining damage points
            // if Choose nothing, apply remaing damage to the hull.

            if (ShipHull != null && DamagePoints > 0)
                DamagePoints = ShipHull.DamageHull(DamagePoints);

            if (DamagePoints > 0)
            {
                //Send Destroyed Event to Quadrant?
                CurrentLocation.PresentShip = null;
                CurrentLocation.DockedShip = null;

                CurrentLocation.Parent.RemoveShip(this);
            }
        }

        public string[] ShipReport()
        {
            List<string> report = GetWeapons().ConvertAll(new Converter<iWeapon, String>(WeaponStatus));
            report.Add(String.Format("Current {0}:{1} class vessel maintains power reserves of {2:#.00} units ", ShipName, ShipModel, cPowerUnit));
            report.Add(String.Format("Current location at {0}", CurrentLocation.ToString()));

            return report.ToArray();
        }

        private static string WeaponStatus(iWeapon arg)
        {
            if (arg != null)
                return arg.WeaponReport();

            return string.Empty; // fail Safe
        }

        public ShipStatus CreateShipStatus()
        {
            return new ShipStatus(this);
        }




        public ShipStatus Research(iShip Target)
        {
            return Target.CreateShipStatus();
        }

        public SpaceUnitInfo Research(iSpaceItem Target)
        {
            throw new NotImplementedException();
        }

        public QuadrantInfo Research(Quadrant Target)
        {
            throw new NotImplementedException();
        }

        public GalaxyInfo Research(Galaxy Target)
        {
            throw new NotImplementedException();
        }
    }
}